#pragma once

#include "ColligereDatabaseTable.hpp"

namespace col {

class DatabaseItemBase;

typedef boost::shared_ptr < DatabaseItemBase > DatabaseItemBasePtr;
typedef std::deque < DatabaseItemBasePtr > DatabaseItemBaseDeque;
typedef long DatabaseItemId;

//! This class represents the current state of an item (like added, changed etc.).
class DatabaseItemState
{
public:
	
	//! Valid state flags.
	enum
	{ 
		ErrMin =				1 << 1,
		None =					1 << 2, 
		Changed =				1 << 3, 
		Added =					1 << 4, 
		Deleted =				1 << 5,
		ChildChanged =			1 << 6,
		ChildAdded =			1 << 7,
		ChildDeleted =			1 << 8,
		ErrMax	=				1 << 9 
	};

	//! Constructor.
	DatabaseItemState();
	/*! @brief Sets the item state flag overriding all previous flags. 
	 * 
	 * @param state The state flag
	 **/
	void Set ( wxUint32 state );
	/*! @brief Adds a new state flag.
	 * 
	 * @param state The state flag
	 **/
	void Add ( wxUint32 state );
	//! Returns true if the state is active, false otherwise.
	bool IsActive ( wxUint32 state ) const;
	//! Returns true if the current state is None, false otherwise.
	bool IsNone() const;
	//! Removes the 'state' from the state flags.
	void Remove ( wxUint32 state );
	//! Returns the "flag container".
	wxUint32 Get() const;

	bool operator == ( wxUint32 state ) const;
	//! @see DatabaseItemState::Set
	DatabaseItemState &operator= ( wxUint32 state );
	//! @see DatabaseItemState::Add
	DatabaseItemState &operator+= ( wxUint32 state );
	//! @see DatabaseItemState::Remove
	DatabaseItemState &operator-= ( wxUint32 state );

#ifdef __WXDEBUG__
	static const wxString ToString ( wxUint32 state );
#endif
private:
	wxUint32 mStateFlags;
};

class DatabaseItemData
{
public:
	/*! @brief Sets the column value.
	 *
	 * @param indx Index of the column.
	 * @param rValue The new value for the column.
	 * @return true if the value was changed or false if it wasn't.
	 **/
	bool SetColumnValue ( wxUint32 indx, const wxString &rValue );
	bool SetColumnValue ( wxUint32 indx, long value );

	/*! @brief Returns a column value.
	 *
	 * @param idx Index of the column
	 * @param rDefaultValue A default value that will be returned if there's 
	 *                      no value at the required index.
	 **/
	const wxString &GetColumnValue ( wxUint32 indx,
		const wxString &rDefaultValue = empty_string ) const;

	/*! @brief Searchers for a column value.
	 *
	 * @param rValCmp The searched value
	 * @param caseSensitive If true, the search will be case sensitive.
	 * @return true if the value was found, false otherwise.
	 **/
	bool HasColumnValue ( const wxString &rValCmp, bool caseSensitive ) const;
	/*! @brief Searches for a column value that matches the given regex.
	 *
	 * @param rRe The regex that will be matched against a column value.
	 * @return true if the value was found, false otherwise.
	 **/
	bool HasColumnValue ( const wxRegEx &rRe ) const;

	/*! @brief Used for checking if a column value has been updated.
	 *
	 * @param index Index of the column
	 * @return true if the column is outdated, false otherwise.
	 **/
	bool IsColumnOutdated ( wxUint32 index ) const;

	size_t GetColumnCount() const;

	//! @see DatabaseItemData::GetColumnValue
	const wxString &operator[] ( wxUint32 idx ) const;

	//! Clears all column values.
	void Clear();
	void ClearColumnUpdateHistory();
private:
	/*! @brief Sets the column value as outdated.
	 *
	 *  @param index The index of the outdated column
	 **/
	void SetColumnAsOutdated ( wxUint32 index );

	// Used with boost foreach.
	typedef std::pair < wxUint32, wxString > ColumnValuePair;
	//! Key = column index and value = column value.
	typedef std::map < wxUint32, wxString > ColumnValueMap;
	
	//! A map for storing column values.
	ColumnValueMap mValues;
	//! A set of column indexes that need updating in the next commit.
	std::set < wxUint32 > mOutdatedColumns;
};

/*! @brief A base class for a database item.
 **/
class DatabaseItemBase
{
public:
	//! Constructor.
	DatabaseItemBase ( const DatabaseTable *pTableDescriptor = 0 );
	//! Copy constructor.
	DatabaseItemBase ( const DatabaseItemBase &rCpy );
	virtual ~DatabaseItemBase();

	DatabaseItemBase &operator= ( const DatabaseItemBase &rAss );

	//! @see DatabaseItemData::SetColumnValue ( wxUint32, const wxString & )
	bool SetColumnValue ( wxUint32 indx, const wxString &rValue );
	//! @see DatabaseItemData::SetColumnValue ( wxUint32, long )
	bool SetColumnValue ( wxUint32 indx, long value );
	//! Sets the item ID.
	void SetId ( DatabaseItemId id );
	/*! @brief Sets the database ID for this item.
	 *
	 * @param id The new ID.
	 * @param failOnInvalidId When true, wxFAIL_MSG() is called when
	 *                        an invalid ID is given.
	 **/
	void SetDatabaseId ( DatabaseItemId id, bool failOnInvalidId = false );

	void SetAsTrashed ( bool trashed = true );

	DatabaseItemId GetId() const;
	/*! @brief A convenience function for easilly getting the parent item's ID.
	 *
	 * @return The parent item's ID or InvalidId if there's no valid parent item.
	 **/
	DatabaseItemId GetParentId() const;
	//! Returns the item's database ID which can be different than the runtime ID.
	DatabaseItemId GetDatabaseId ( bool failOnError = false ) const;
	DatabaseItemId GetParentDatabaseId ( bool failOnError = false ) const;

	//! @see DatabaseItemData::GetColumnValue
	const wxString &GetColumnValue ( wxUint32 indx,
		const wxString &rDefaultValue = empty_string ) const;

	//! @see DatabaseItemData::GetColumnValue
	const wxString &operator[] ( wxUint32 idx ) const;
	
	size_t GetColumnCount() const;

	virtual bool operator == ( const DatabaseItemBase &rCmp ) const;
	virtual bool operator == ( DatabaseItemId id ) const;

	bool IsTrashed() const;

	/*! @brief Initializes column data from an wxSQLite3ResultSet object.
	 *
	 * @param rColumnData The result set for initializing this item.
	 * @return *this
	 **/
	virtual DatabaseItemBase &operator<< ( wxSQLite3ResultSet &rColumnData );

	DatabaseItemState &GetState();
	const DatabaseItemState &GetState() const;

	/*! @brief Sets the item state flag overriding all previous flags.
	 *
	 * If the new state flag is DatabaseItemState::None then 
	 * the column update history is also resetted.
	 **/
	void SetState ( wxUint32 state );

	virtual void AddChild ( const DatabaseItemBase &rChild );
	
	//! Returns the number of child items for this item.
	virtual size_t GetChildCount() const;
	
	virtual const DatabaseItemBase *GetLastChild() const;
	virtual const DatabaseItemBase *GetFirstChild() const;

	/*! @brief Finds a child item.
	 *
	 * @param childId The ID of the child item
	 * @return A pointer to the child item or 0 if not found.
	 **/
	virtual DatabaseItemBase *FindChild ( wxUint32 childId );
	//! @see DatabaseItemBase *FindChild ( wxUint32 childId )
	virtual const DatabaseItemBase *FindChild ( wxUint32 childId ) const;
	
	//! Clears the child item list.
	virtual void ClearChildren();

	const DatabaseItemBaseDeque &GetChildren() const;
	DatabaseItemBaseDeque &GetChildren();

	virtual const DatabaseItemBase &GetParent() const;

	/*! @brief Copies column data to another item.
	 *
	 * @param pTo The item to which the data will be copied.
	 * @param rIgnoreCols A deque of column indexes that will not be copied.
	 * @return The number of pTo's columns that were changed.
	 **/
	wxUint32 CopyData ( DatabaseItemBase *pTo, 
		const std::deque < wxUint32 > &rIgnoreCols = 
		std::deque < wxUint32 >() ) const;

	virtual const DatabaseTable &GetTableDescriptor() const;

	/*! @brief Formats the item to an SQL UPDATE query.
	 *
	 * @return The query or an empty string on error.
	 **/
	virtual const wxString ToSqlUpdateQuery() const;
	/*! @brief Formats the item to an SQL INSERT query.
	 *
	 * @return The query or an empty string on error.
	 **/
	virtual const wxString ToSqlInsertQuery() const;
	/*! @brief Formats the item to an SQL DELETE query.
	 *
	 * @return The query or an empty string on error.
	 **/
	virtual const wxString ToSqlDeleteQuery() const;
	/*! @brief Formats the item to an SQL DELETE children query.
	 *
	 * @return The query or an empty string if there are not child items.
	 **/
	virtual const wxString ToSqlDeleteChildrenQuery() const;

	//! @see DatabaseItemData::HasColumnValue ( const wxString, bool caseSensitive ) const
	bool HasColumnValue ( const wxString &rValCmp, bool caseSensitive ) const;
	//! @see DatabaseItemData::HasColumnValue ( const wxRegEx ) const
	bool HasColumnValue ( const wxRegEx &rRe ) const;

	bool IsNew() const;
	bool IsChanged() const;
	bool IsDeleted() const;

	enum { InvalidItemId = 0 };

	//! Returns a dynamically allocated copy of *this.
	virtual DatabaseItemBase *Clone() const = 0;
protected:
	void SetParent ( DatabaseItemBase *pItem );
private:
	//! Returns an unique runtime ID.
	static DatabaseItemId GenerateRuntimeId();

	/*! @brief Unique runtime ID.
	 * 
	 * Must never be == InvalidItemId.
	 **/
	DatabaseItemId mRuntimeId;

	DatabaseItemData mData;
	DatabaseItemState mState;

	//! The parent item for this item (if any).
	DatabaseItemBase *mpParent;

	//! Deque of child items for this items (if any).
	DatabaseItemBaseDeque mChildren;

	const DatabaseTable *mpTableDescriptor;
};

struct db_item_equal_id : public std::binary_function 
	< DatabaseItemBasePtr, DatabaseItemId, bool >
{
	bool operator()	( const DatabaseItemBasePtr item, DatabaseItemId id ) const
	{
		return item->GetId() == id;
	}
};

}